package xyz.thoughtset.viewer.modules.ds.http.core.linker;


import lombok.SneakyThrows;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import xyz.thoughtset.viewer.common.connector.entity.bo.ConnectorMeta;
import xyz.thoughtset.viewer.common.connector.entity.bo.Linker;
import xyz.thoughtset.viewer.common.connector.linker.ConnBuilder;
import xyz.thoughtset.viewer.modules.ds.http.core.enums.OkHttp3BuilderEnum;
import xyz.thoughtset.viewer.modules.ds.http.core.req.SyncHttpReq;

import javax.net.ssl.*;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public abstract class HttpConnBuilder extends ConnBuilder {
    private static final Properties EMPTY_PROP = new Properties();

    public HttpConnBuilder(Linker dataLinker) {
        super(dataLinker);
    }

    @SneakyThrows
    @Override
    public Object buildConnect(ConnectorMeta connectorMeta, Properties properties){
        if (properties == null) {
            properties = EMPTY_PROP;
        }
        Object connections = properties.get(OkHttp3BuilderEnum.MAX_IDLE_CONNECTIONS.getTitle());
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (Objects.nonNull(connections)){
            if (connections instanceof String){
                connections = Integer.parseInt((String) connections);
            }
            if(((Integer) connections) < 3){
                connections = 3;
            }
            Object keepAliveDuration = properties.get(OkHttp3BuilderEnum.KEEP_ALIVE_DURATION.getTitle());
            if(Objects.isNull(keepAliveDuration)){
                keepAliveDuration = 5L;
            }else {
                if (keepAliveDuration instanceof String){
                    keepAliveDuration = Long.parseLong((String) keepAliveDuration);
                }
            }
            if(((Long) keepAliveDuration) < 2L){
                connections = 2L;
            }
            builder.connectionPool(
                    new ConnectionPool(
                            (Integer) connections,
                            (Long) keepAliveDuration,
                            TimeUnit.MINUTES));
        }
        if (!Boolean.valueOf(OkHttp3BuilderEnum.getStrVal(properties, OkHttp3BuilderEnum.ENABLE_VERIFY_SSL))) {
            builder.hostnameVerifier((hostname, session) -> true);
            X509TrustManager myTrustManager = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {

                }
                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws java.security.cert.CertificateException {
                }
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
            SSLContext sslCtx = SSLContext.getInstance("TLS");
            sslCtx.init(null, new TrustManager[] { myTrustManager }, new SecureRandom());
            builder.sslSocketFactory(sslCtx.getSocketFactory(),myTrustManager);
        }
        Integer connectTimeout = OkHttp3BuilderEnum.getIntVal(properties, OkHttp3BuilderEnum.CONNECT_TIMEOUT);
        Integer readTimeout = OkHttp3BuilderEnum.getIntVal(properties, OkHttp3BuilderEnum.READ_TIMEOUT);
        Integer writeTimeout = OkHttp3BuilderEnum.getIntVal(properties, OkHttp3BuilderEnum.WRITE_TIMEOUT);
        if (connectTimeout != null && connectTimeout > 0) {
            builder.connectTimeout(connectTimeout, TimeUnit.SECONDS);
        }
        if (readTimeout != null && readTimeout > 0) {
            builder.readTimeout(readTimeout, TimeUnit.SECONDS);
        }
        if (writeTimeout != null && writeTimeout > 0 && writeTimeout < 60 * 1000 * 10L
                && writeTimeout > readTimeout + 5L) {
            builder.writeTimeout(writeTimeout, TimeUnit.SECONDS);
        }
        String proxyHost = OkHttp3BuilderEnum.getStrVal(properties, OkHttp3BuilderEnum.PROXY_HOST);
        Integer proxyPort = OkHttp3BuilderEnum.getIntVal(properties, OkHttp3BuilderEnum.PROXY_PORT);
        if (proxyHost != null && !proxyHost.isEmpty() && proxyPort != null) {
            if (proxyPort < 1 || proxyPort > 65535) {
                throw new IllegalArgumentException("Invalid proxy port: " + proxyPort);
            }
            if (proxyHost.length() > 255) {
                throw new IllegalArgumentException("Invalid proxy host: " + proxyHost);
            }
            if (proxyHost.contains(":")) {
                throw new IllegalArgumentException("Invalid proxy host: " + proxyHost);
            }
            Proxy.Type type = Proxy.Type.HTTP;
            if (OkHttp3BuilderEnum.getStrVal(properties, OkHttp3BuilderEnum.PROXY_TYPE).equalsIgnoreCase("SOCKS")) {
                type = Proxy.Type.SOCKS;
            }
            Proxy proxy = new Proxy(type, new InetSocketAddress(proxyHost, proxyPort));
            builder.proxy(proxy);
        }

        return new SyncHttpReq(true,connectorMeta.getUrl(),builder.build());
    }


//    private Authenticator getProxyAuthenticator(String username, String password) {
//        return (route, response) -> {
//            String credential = okhttp3.Credentials.basic(username, password);
//            return response.request().newBuilder()
//                    .header("Proxy-Authorization", credential)
//                    .build();
//        };
//    }

    protected abstract OkHttpClient.Builder addInterceptor(OkHttpClient.Builder builder, ConnectorMeta connectorMeta, Properties properties);

}
